he parameter you use, the safer your shutdown will be. If you have to resort to 0 (re-boot), you will have the long wait for boot up associated with turning the computer off then on. Memory Functions: Block Move Moves blocks of memory. Requires three parameters: source address, destination address, and length. Enter these three address after one another on the line and hit return. Block Compare Compares blocks of memory using the same three parameters as the Block Move command. Any differences will be displayed as "Mismatch at xxxx/yyyy" where xxxx is the address of the source and yyyy is the address of the destination where the blocks do not match. Fill Fills a block of memory with a specified value. Takes four parametes with the fourth being optional: beginning address, ending address, fill value, and optionally, the size of the fill value - 1 for byte fill, 2 for word fill, and 4 for long word fill. Find Finds a specified value in a specified range of memory. Takes four parameters: search value, search value size (same as size from Fill command above), start address and end address. If any matches are found, they will be displayed between the curly braces. Template Displays a memory location as if it were a Mac data structure, showing you all the current values. TMON currently knows only four data structures: WindowRecord, ControlRecord, TERecord, and ParamBlock (see IM for descriptions of these). Clicking on the Template line hitting Return will cycle through these four templates. Template takes one parameter, an address. So, after finding the structure you wish to display, enter an address that contains a structure of that type and hit return. TMON will list all current values for the fields of that structure. Note that information will be meaningless unless there is actually a structure of the desired type at the address you specify. This command could be helpful in looking at key disk checkers by allowing you to look at the ParamBlock the program is currently using to read the disk - although I have never used this. Stack addresses Attempts to recognize as labels the supplied address. This function defaults to an address of SP - stack pointer. To use this, just click to left of SP and hit return. The function will then look at the first address on the stack and see if it matches any labels that it currently knows. For example, right after a JSR, the stack contains the return address. If TMON k knows a label for the return address (and it will if there was a label to the left of the JSR in the assembly window) then using the Stack Addresses command will display the label in curly braces. Hitting return repeatedly will then move up the stack, analysing each successive stack address. Click in front of the SP and hit return to reset the command to the original stack pointer. Stack crawl Attempts to find the return address of a procedure that has a currently active stack frame. Remember that most compiled programs use the LINK and UNLK instructions to set up stack frames to temprarily store local variables. If you know what register is being used as the stack frame pointer (A6 is the only one I have ever seen and this is the default value TMON uses), then the Stack Crawl can use that register to analyse the stack and try to determine the return address and display it in the curly braces. Load resource Loades the resource specified by the two parameters (type and id #) into memory and displays the address of the resource in the curly braces. Print Allows you to print listings longer that contained in the active window. Clicking on the Print line and hitting return toggles the print mode between Dump, Assembly, File, and Heap. Once the mode has been selected, the print command needs a start and end address. Type these in, hit return, and TMON will print the desired output to the serial port (meaning that you cannot use a laserwriter, but you can use an imagewriter.) A000 Trap Functions: Trap record Unknown. Allows you to record any traps called by the program, but requires a lot of complicated set up. Record Unknown. Used to allocate a table for trap record (above). Trap Unknown. Used by programmers to test the heap anytime a trap that affects the heap zone is called. We don't need this to crack. Heap Unknown. Similar to Trap but doesn't wait for a trap to execute. We don't need this one either. Trap discipline A programmer's feature. Trap discipline is a means of checking traps for faulty parameters. Select a range of traps and a PC range (see Trap Intercept below) and TMON will check all traps within that range. If it finds a trap with questionable parameters, the monitor will be entered. There are two strengths of discipline: lenient and strict. To toggle these, click on the trap discipline line and hit return. Trap checksum Unknown. Another function that programmers would use to check application problems. Since we are cracking an application that already works, we don't need this one. Checksum Unknown. Used to specify the checksum for Trap Checksum. Trap intercept Allows you to specify a trap or range of traps that, when encountered, will cause the monitor to be entered. Simply click on the line and enter the trap name WITH a leading underscore, a space, and then the second trap. This specifies a range of traps to look for, the range being in numerical order of trap numbers. If only one trap is specified, only that trap will be checked. Use this to catch a program that uses a dialog to prompt for a serial number. If the trap entered is _ModalDialog, the monitor will be entered just before the dialog is drawn. Optionally, a PC range may be entered after the trap range. This would specify that TMON regain control only if the specified trap is encountered within the specified PC range. I have never used a PC range. Trap signal Similar to Trap Interrupt, except that once the trap range and optional PC range have been entered, the user must hit the interrupt switch to enter the monitor. Once the interrupt switch (or Programmer's Key) has been pressed, TMON will continue execution until a trap within the specified range has been encountered. Options / Cmd-o Allows setting of seven monitor global functions. I am not exactly clear what the various settings mean, so I jus leave them all on. Print / Cmd-p Causes the active window to be dumped to whatever port has been set during setup (achieved by launching the TMON application). This prints a window's contents only! To get long printouts, use the print command in the User Window. How to crack Sorcerer We are now going to look at a typical key-disk protection scheme. The important concepts to grasp here are how to quickly isolate the protection, and then how to remove it. Don't worry too much about the particulars, unless you happen to have a copy of Sorcerer you want to crack. First off, how do we know that it is protected? Dumb question, but this is really important to beginning the crack. With Sorcerer, we note that when launched from the hard drive, it brings up a dialog box (or alert) requesting the key disk. So the logical place to start is with Resedit to try and figure out what resource the program is using to display the alert. After you open the application in Resedit, we see the following:  Well, there are no ALRT or DLOG resources, so the program is generating its own dialog internally. If there was a set of ALRT or DLOG resources, we would quickly scan them and try to determine which was the one that the program displays to request the key-disk. If we could locate the ID # of the correct DLOG resource, we would go into Nosy and bring up the Traps ref map, see which procs called GetNewDialog, or Alert (if the resource in question was ALRT), and then check all the procs Nosy listed to see which one called GetNewDialog with the ID # we had found in Resedit. Often you will find that there are DLOGs or ALRTs, but none of them have the correct message. If this is the case, then we would be in the same spot we are right now. The next thing to consider is that the string "Please Insert the Original Disk" (or whatever the string is) has to come from somewhere. You can try to locate it in Nosy, but often the string will be in a string resource. Look at the Resedit window above, and note the STR resource. Let's take a look:  Perfect! There is the culprit. So, all we have to do is find the part of Sorcerer that uses STR resource # 256. Since there are several ways to load a string, you might want to forget the Traps ref map and start tracing the program. If the program is huge, this might not be the way to go. If you look at the Traps ref map for Sorcerer, you would eventually find that proc108 calls the trap GetString. This would be an excellent place to start. Otherwise you might just find the proc called Sorcerer and start tracing there...An important note: tracing programs from start to error sucks. If you can figure out which trap is causing the problem, then by all means do so. If you are not familiar enough with the various traps, then you might well have to trace. Get a hold of IM and learn the Dialog Manager and the Resource Manager! OK, let's start with the procedure Sorcerer: D50: QUAL Sorcerer ; b# =59 s#1 =proc38 D50: 4EBA 004C 1000D9E Sorcerer JSR proc39 D54: 4E56 0000 'NV..' LINK A6,#0 D58: 2C5F ',_' POP.L A6 D5A: 4E55 FCB6 'NU..' LINK A5,#-$34A D5E: 9FED 0010 $10 SUBA.L glob27(A5),A7 D62: 4EBA 0042 1000DA6 JSR proc41 D66: 41ED FCB2 -$34E LEA glob2(A5),A0 D6A: 2F08 '/.' PUSH.L A0 D6C: 4EBA F292 1000000 JSR proc1 D70: A8FE '..' _InitFonts D72: 3F3C FFFF '?<..' PUSH #$FFFF D76: 4267 'Bg' CLR -(A7) D78: 4EBA F49A 1000214 JSR FlushEvents D7C: A912 '..' _InitWindows D7E: A9CC '..' _TeInit D80: 42A7 'B.' CLR.L -(A7) D82: A97B '.{' _InitDialogs ; (resumeProc:ProcPtr) D84: A850 '.P' _InitCursor D86: 4EAD 0092 20003F6 JSR proc108(A5) D8A: 4EBA 0390 100111C JSR proc54 D8E: 4EBA 0156 1000EE6 JSR %_TERM D92: 4E5D 'N]' UNLK A5 D94: 4EBA 000E 1000DA4 JSR proc40 D98: 4E75 'Nu' RTS D9A: 4E5E data20 DC.B 'N^Nu' A quick scan should reveal that possible problem areas are proc39, proc41, proc1, proc108, and proc54 since these are procedures that we can't see from this listing which is normal enough by itself. Luckily, if you were to look at the first three procs called, they are very short and very benign. If these were long, complex procedures, I might seriously consider going into TMON and setting a Trap Intercept to pick up _InitFonts so that TMON would grab control of the program early. Then when I launch Sorcerer, if TMON breaks in then the error is later in the program, but if Sorcerer bombs, then the error was before the InitFonts. That is a quick way to locate the problem. So, let's take a look at the next procedure, proc108: 3F6: QUAL proc108 ; b# =194 s#2 =proc108 vem_1 VEQU -1040 vem_2 VEQU -1038 vem_3 VEQU -1036 vem_4 VEQU -1034 vem_5 VEQU -1030 vem_6 VEQU -774 vem_7 VEQU -512 3F6: VEND ;-refs - 1/proc37 1/Sorcerer 3F6: 4A6F EBE6 'Jo..' proc108 TST -$141A(A7) 3FA: 4E56 FBE6 'NV..' LINK A6,#-$41A 3FE: 48E7 0F18 'H...' MOVEM.L D4-D7/A3-A4,-(A7) 402: 41EE FE00 200FE00 LEA vem_7(A6),A0 406: 2848 '(H' MOVEA.L A0,A4 408: 486E FBFA 200FBFA PEA vem_5(A6) 40C: 486E FBF0 200FBF0 PEA vem_1(A6) 410: 486E FBF6 200FBF6 PEA vem_4(A6) 414: A9F5 '..' _GetAppParms ; (VAR apName:Str255; VAR apRefNum:INTEGER; VAR apParam:Handle) 416: 4267 'Bg' CLR -(A7) 418: 41EE FCFA 200FCFA LEA vem_6(A6),A0 41C: 2F08 '/.' PUSH.L A0 41E: 486E FBF2 200FBF2 PEA vem_2(A6) 422: 4EAD 0052 1000162 JSR GetVol(A5) 426: 3E1F '>.' POP D7 428: 4267 'Bg' CLR -(A7) 42A: 486E FBFA 200FBFA PEA vem_5(A6) 42E: 3F2E FBF2 200FBF2 PUSH vem_2(A6) 432: 3F3C 0010 '?<..' PUSH #16 436: 2F0C '/.' PUSH.L A4 438: 4267 'Bg' CLR -(A7) 43A: 4EBA FDBE 20001FA JSR proc103 43E: 181F '..' POP.B D4 440: 4267 'Bg' CLR -(A7) 442: 3F2E FBF2 200FBF2 PUSH vem_2(A6) 446: 2F0C '/.' PUSH.L A4 448: 4EBA FED8 2000322 JSR proc105 44C: 101F '..' POP.B D0 44E: 0A00 0001 '....' EORI.B #1,D0 452: 6700 00A0 20004F4 BEQ lem_2 456: 4267 'Bg' CLR -(A7) 458: 3F3C 0002 '?<..' PUSH #2 45C: 3F2E FBF2 200FBF2 PUSH vem_2(A6) 460: 2F0C '/.' PUSH.L A4 462: 4EBA FF0C 2000370 JSR proc106 466: 101F '..' POP.B D0 468: 0A00 0001 '....' EORI.B #1,D0 46C: 6700 0086 20004F4 BEQ lem_2 470: 4267 'Bg' CLR -(A7) 472: 3F3C 0001 '?<..' PUSH #1 476: 3F2E FBF2 200FBF2 PUSH vem_2(A6) 47A: 2F0C '/.' PUSH.L A4 47C: 4EBA FEF2 2000370 JSR proc106 480: 101F '..' POP.B D0 482: 0A00 0001 '....' EORI.B #1,D0 486: 676C 20004F4 BEQ.S lem_2 488: 42A7 'B.' CLR.L -(A7) 48A: 3F3C 0101 '?<..' PUSH #257 48E: 42A7 'B.' CLR.L -(A7) 490: 70FF 'p.' MOVEQ #-1,D0 492: 2F00 '/.' PUSH.L D0 494: A9BD '..' _GetNewWindow ; (windowID:INTEGER; wStorage:Ptr; behind:WindowPtr):WindowPtr 496: 265F '&_' POP.L A3 498: 2F0B '/.' PUSH.L A3 49A: A873 '.s' _SetPort ; (port:GrafPtr) 49C: 3F3C 0010 '?<..' PUSH #16 4A0: 3F3C 001C '?<..' PUSH #28 4A4: A893 '..' _MoveTo ; (h,v:INTEGER) 4A6: 4267 'Bg' CLR -(A7) 4A8: A887 '..' _TextFont ; (font:FontCode) 4AA: 42A7 'B.' CLR.L -(A7) 4AC: 3F3C 0100 '?<..' PUSH #256 4B0: A9BA '..' _GetString ; (stringID:INTEGER):StringHandle 4B2: 2C1F ',.' POP.L D6 4B4: 2046 ' F' MOVEA.L D6,A0 4B6: 2F10 '/.' PUSH.L (A0) 4B8: A884 '..' _DrawString ; (s:Str255) 4BA: 4267 'Bg' CLR -(A7) 4BC: 42A7 'B.' CLR.L -(A7) 4BE: 3F3C 0001 '?<..' PUSH #1 4C2: 4EAD 002A 1000186 JSR Eject(A5) 4C6: 3E1F '>.' POP D7 4C8: 486E FBF4 200FBF4 lem_1 PEA vem_3(A6) 4CC: 4EBA FEEE 20003BC JSR proc107 4D0: 4267 'Bg' CLR -(A7) 4D2: 3F2E FBF4 200FBF4 PUSH vem_3(A6) 4D6: 2F0C '/.' PUSH.L A4 4D8: 4EBA FE48 2000322 JSR proc105 4DC: 1A1F '..' POP.B D5 4DE: 4267 'Bg' CLR -(A7) 4E0: 42A7 'B.' CLR.L -(A7) 4E2: 3F2E FBF4 200FBF4 PUSH vem_3(A6) 4E6: 4EAD 002A 1000186 JSR Eject(A5) 4EA: 3E1F '>.' POP D7 4EC: 1005 '..' MOVE.B D5,D0 4EE: 67D8 20004C8 BEQ lem_1 4F0: 2F0B '/.' PUSH.L A3 4F2: A914 '..' _DisposWindow ; (theWindow:WindowPtr) 4F4: 4CDF 18F0 'L...' lem_2 MOVEM.L (A7)+,D4-D7/A3-A4 4F8: 4E5E 'N^' UNLK A6 4FA: 4E75 'Nu' RTS 4FC: '............' data76 DC.W $8100,8,0,$4FC,$FC00,0 The first thing to do here is to quickly scan for trap names. There are quite a few, but one should stick out. Remember that we are looking for some reference to STR #256. Note the GetString trap. Immediately before the trap is a PUSH #256...that's our guy! So, at this point, we know where the string is being loaded and drawn. Since this procedure is called from the Main procedure, we can bet that the key-disk check is also in this proc. Note that this is not always the case - often when you find the procedure that loads the dialog or string, you need to back trace to find out where the actual error generator is located. That is where the Refs line (right below the VEND) in the listing comes in handy. Note that this proc is called by not only Sorcerer, but also by proc37. This might mean that the program checks the key-disk later in its execution. But if you load up proc37, you would find that it simply Unloads the segment so it is harmless. At this point, all we need to do is disable the disk-check. So, start scanning down the listing and ask yourself "Where is a branch that will skip over the GetString trap?". If you find that branch and make it always branch then odds are the program is cracked. Nosy will help out here. We are looking for a spot in the listing that a branch can jump to that will skip over the error. We have two choices in this listing: lem_1 and lem_2. Check out lem_1, and you will see a couple of problems with it. First of all, see what piece of code branches to it. There is a JSR Eject, then a test, and a BEQ lem_1. Also note that there is a DisposeWindow after it. We might guess that DisposeWindow is disposing the error dialog. We might also guess that lem_1 is being used as a loop to eject bad disks and request key-disks. Well, let's give lem_2 a shot. Now this one looks good - it is located right down at the procedures exit, so, if something is branching here, all the above stuff gets skipped. So, just select lem_2 and hit cmd-f to let Nosy find all the references to lem_2 in the listing. Line 452 is the key. Note, D0 gets a result from an unknow procedure, then is EORd with 1, and then the branch occurs. It sure looks like changing that branch from BEQ to BRA would gurrantee that the error never occured. Let's try it. From the assembly instruction listing, we see that BEQ is 67, and BRA is 60. So, look at the first line in the above listing and we see that it is segment 2. So, open CODE resource 2 in Resedit, and skip down to address 456 (remember, take the Nosy address and add 4 to find the Resedit address).  There it is, on line 450. See the 6700? That sure matches what we find in Nosy, so that is our guy. Change the 67 to 60 by clicking to the right of the 67, hitting backspace or delete, and typing 60. That's it! Now quit Resedit and save changes. Launch the program and the protection is gone! Let me quickly mention one last thing. The above crack involved looking for a branch that would skip over the problem area, and making damn sure that that branch always executed. But suppose that the program was setup so that after the disk check, the program branched to the error section. In this case, we would want to make sure the branch never executed. There are two ways to do this. First off, you can change the branch to its logical opposite - BCC to BCS, BNE to BEQ, etc. That way, the condition that triggers the error will now trigger the opposite, and run properly. The second method is to simply replace the trap with a NOP. That way, the branch never executes no matter what happens. Look for upcoming material on more specific cracking methods and more actual cracks. later - The Shepherd